#include "baseDB.h"
#include <map>
#include <tcl.h>
#include <pthread.h>
#include <unistd.h>
#include <fcntl.h>
#include <string>
#include <linux/stat.h>
#include <stdio.h>
#include <string.h>

#define FIFO_FILE       "/tmp/CG_PIPE"
#define LIMIT   1024

using namespace std;

double GRID = 0.8;
double CLB_SIZE = 0.5;

extern vector<unsigned int> V_CHANGEs;
extern vector<C_CELL*> V_CELLs;
extern unsigned int XDIM,YDIM,ZDIM;
extern unsigned int XFREQ,YFREQ,TDMDIST;
extern vector< vector<int> > TDM_LOC;

vector<C_CELL> v_clbs;

FILE *PIPE_FP;
int PIPE_FD;
const char* color_map[6] = {"BLACK","BLUE","RED","GREEN","YELLOW","GREY"};
int COUNT = 0;

bool _get_rgb_code(const char* ColorName,float& r,float& g,float& b);
//////////////////////
string SUB_CMDs;

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
void *pipe_send_cmd_service(void* ptr);
//////////////////////

int cg3d_init (ClientData clientData,
                Tcl_Interp *interp,
                int argc, const char *argv[])
{
    PIPE_FP = fopen(FIFO_FILE, "w");
    pthread_t thread1;
    pthread_create( &thread1, NULL, pipe_send_cmd_service, NULL);
}

int cg3d_FlushCMDs (ClientData clientData,
                Tcl_Interp *interp,
                int argc, const char *argv[])
{
    return TCL_OK;
    //printf("cg3d_FlushCMDs %d\n",SUB_CMDs.size());
    //PIPE_FP = fopen(FIFO_FILE, "w");
    //PIPE_FD = open(FIFO_FILE,O_WRONLY);
    //fputs(SUB_CMDs.data(), PIPE_FP);
    //printf("%d\n",write(PIPE_FD,SUB_CMDs.data(),SUB_CMDs.size()));
    //fflush(PIPE_FP);
    //SUB_CMDs.clear();
    //fclose(PIPE_FP);
    //close(PIPE_FD);
}

// Add clb to 3d space
// cg3d_AddCLB str_name f_x f_y f_z
int cg3d_AddCLB (ClientData clientData,
                Tcl_Interp *interp,
                int argc, const char *argv[])
{

    printf("cg3d_AddCLB %s %s %s %s\n",argv[1],argv[2],argv[3],argv[4]);
    double x,y,z;
    string name;
    int type;
    E_CELL_TYPE mytype;

    if (argc!=5)
        return TCL_ERROR;

    name.assign(argv[1]);

    if (Tcl_GetDouble(interp,argv[2],&x)!=TCL_OK) {
        printf("Error: can not get x %s\n",argv[2]);
        return TCL_ERROR;
    }
    if (Tcl_GetDouble(interp,argv[3],&y)!=TCL_OK) {
        printf("Error: can not get y %s\n",argv[3]);
        return TCL_ERROR;
    }
    if (Tcl_GetDouble(interp,argv[4],&z)!=TCL_OK) {
        printf("Error: can not get z %s\n",argv[4]);
        return TCL_ERROR;
    }
    //if (Tcl_GetInt(interp,argv[5],&type)!=TCL_OK) {
    //    type = 0;
    //}
    type = 0;

    if (type == 0) //clb
        mytype = e_clb;
    else if (type == 1) //input io
        mytype = e_ioin;
    else if (type == 2) //output io
        mytype = e_ioout;

    // set C_CELL
    C_CELL clb(name.data(),mytype);
    clb.setLoc((float)x,(float)y,(float)z);

    v_clbs.push_back(clb);
    
    // submit cmd to 3d cg
    char mycmd[128];
    string strCmd;
    memset(mycmd, 0, sizeof(mycmd));
    
    sprintf(mycmd,"AddCube(%s,%f,%f,%f);",argv[1],x,y,z);
    
    PIPE_FD = open(FIFO_FILE,O_WRONLY);
    //printf("%s %d\n",mycmd,COUNT);
    //COUNT++;
    write(PIPE_FD,mycmd,sizeof(mycmd));
    close(PIPE_FD);
    
    return TCL_OK;
}

int cg3d_SetCLBLoc (ClientData clientData,
                Tcl_Interp *interp,
                int argc, const char *argv[]) {

    double x,y,z;
    string name;

    name.assign(argv[1]);

    if (Tcl_GetDouble(interp,argv[2],&x)!=TCL_OK) {
        printf("Error: can not get x %s\n",argv[2]);
        return TCL_ERROR;
    }
    if (Tcl_GetDouble(interp,argv[3],&y)!=TCL_OK) {
        printf("Error: can not get y %s\n",argv[3]);
        return TCL_ERROR;
    }
    if (Tcl_GetDouble(interp,argv[4],&z)!=TCL_OK) {
        printf("Error: can not get z %s\n",argv[4]);
        return TCL_ERROR;
    }

    // submit command to pipe
    char mycmd[128];
    memset(mycmd, 0, sizeof(mycmd));
    
    sprintf(mycmd,"SetCubeLoc(%s,%f,%f,%f);",argv[1],x,y,z);

    PIPE_FD = open(FIFO_FILE,O_WRONLY);
    write(PIPE_FD,mycmd,sizeof(mycmd));
    close(PIPE_FD);

    return TCL_OK;
}

int cg3d_AddEdge (ClientData clientData,
                Tcl_Interp *interp,
                int argc, const char *argv[]) {
    
    // submit command to pipe
    char mycmd[128];
    memset(mycmd, 0, sizeof(mycmd));
    
    sprintf(mycmd,"AddEdge(%s,%s);",argv[1],argv[2]);

    PIPE_FD = open(FIFO_FILE,O_WRONLY);
    write(PIPE_FD,mycmd,sizeof(mycmd));
    close(PIPE_FD);

    return TCL_OK;
}

// Highlight CLB
int cg3d_hilitCLB(ClientData clientData,
                Tcl_Interp *interp,
                int argc, const char *argv[])
{

    printf("cg3d_hilitCLB %s %s\n",argv[1],argv[2]);
    if (argc<2) {
        printf("Usage: clbname [color] # default color = red");
        return TCL_ERROR;
    }

    string name(argv[1]);
    int idx;
    int coloridx;
    float r = 0.0,g = 0.0,b = 0.0;

    bool isFound = false;
    printf("ori: %s\n",name.data());
    for (idx=0;idx<v_clbs.size();idx++) {
        printf(" -> %s\n",v_clbs[idx].getName());
        if (name.compare(v_clbs[idx].getName()) == 0) {
            isFound = true;
            break;
        }
    }
    
    if (isFound == false) {
        printf("Error: Can not find CLB: %s\n",argv[1]); 
        return TCL_ERROR;
    }

    if (!_get_rgb_code(argv[2],r,g,b)) {
        printf("Error: Can not find color: %s in color map\n",argv[2]);
        return TCL_ERROR;
    }

    // submit command to pipe
    char mycmd[128];
    memset(mycmd, 0, sizeof(mycmd));
    
    sprintf(mycmd,"SetCubeColor(%s,%f,%f,%f);",argv[1],r,g,b);

    PIPE_FD = open(FIFO_FILE,O_WRONLY);
    write(PIPE_FD,mycmd,sizeof(mycmd));
    close(PIPE_FD);

    return TCL_OK;
}

int cg3d_Display(ClientData clientData,
                Tcl_Interp *interp,
                int argc, const char *argv[])
{
    //pthread_t thread;
    //int iret;
    
    char* argv1[2] = {"a","b"};
    //QApplication a(argc,argv1);
    //MainWindow w;
    //w.show();

    //iret = pthread_create
    //a.exec();
    //iret = pthread_create( &thread, NULL, batch_gui_launcher, &a);
    return TCL_OK;
}

int cg3d_updateLoc(ClientData clientData,
                Tcl_Interp *interp,
                int argc, const char *argv[]) {
    
    char mycmd[128];
    float x,y,z;

    for (unsigned int i=0;i<V_CHANGEs.size();i++) {
        V_CELLs[V_CHANGEs[i]]->getLoc(x,y,z);

        // submit command to pipe
        memset(mycmd, 0, sizeof(mycmd));
        sprintf(mycmd,"SetCubeLoc(%s,%f,%f,%f);",V_CELLs[V_CHANGEs[i]]->getName(),x,y,z);

        PIPE_FD = open(FIFO_FILE,O_WRONLY);
        write(PIPE_FD,mycmd,sizeof(mycmd));
        close(PIPE_FD);

    }

    V_CHANGEs.clear();
    return TCL_OK;
}

// add net segment
int cg3d_add_netseg(ClientData clientData,
                Tcl_Interp *interp,
                int argc, const char *argv[]) {
    
    char mycmd[128];
    double x0,y0,z0,x1,y1,z1,trackIdx;
    double x,y,z,track;
    double r,g,b;
    string type(argv[1]);

    if (Tcl_GetDouble(interp,argv[2],&x)!=TCL_OK) {
        printf("Error: can not get x %s\n",argv[2]);
        return TCL_ERROR;
    }
    if (Tcl_GetDouble(interp,argv[3],&y)!=TCL_OK) {
        printf("Error: can not get y %s\n",argv[3]);
        return TCL_ERROR;
    }
    if (Tcl_GetDouble(interp,argv[4],&z)!=TCL_OK) {
        printf("Error: can not get z %s\n",argv[4]);
        return TCL_ERROR;
    }
    if (Tcl_GetDouble(interp,argv[5],&track)!=TCL_OK) {
        printf("Error: can not get track %s\n",argv[5]);
        return TCL_ERROR;
    }

    if (argc>6) {
        if (Tcl_GetDouble(interp,argv[6],&r)!=TCL_OK) {
            printf("Error: can not get r %s\n",argv[6]);
            return TCL_ERROR;
        }
        if (Tcl_GetDouble(interp,argv[7],&g)!=TCL_OK) {
            printf("Error: can not get g %s\n",argv[7]);
            return TCL_ERROR;
        }
        if (Tcl_GetDouble(interp,argv[8],&b)!=TCL_OK) {
            printf("Error: can not get b %s\n",argv[8]);
            return TCL_ERROR;
        } 
    }
    
    // init
    x0 = x;
    x1 = x;
    y0 = y;
    y1 = y;
    z0 = z;
    z1 = z;
    trackIdx = track;

    if (type.compare("CHANX") == 0) {
        x0 = x - CLB_SIZE;
        x1 = x + CLB_SIZE;
        y0 = y + GRID/2;
        y1 = y0;
    } else if (type.compare("CHANY") == 0) {
        y0 = y - CLB_SIZE;
        y1 = y + CLB_SIZE;
        x0 = x + GRID/2;
        x1 = x0;
    } else if (type.compare("CHANZ") == 0) {
        x0 = x+0.5;
        y0 = y+0.5;
        bool found = false;
        for (int i=0;i<TDM_LOC.size();i++) {
            printf("LOC: %d %d (%d %d)\n",TDM_LOC[i][0],TDM_LOC[i][1],(int)x,(int)y);
            //Bottom Left
            if ( (TDM_LOC[i][0] == (int)x) && (TDM_LOC[i][1] == (int)y)) {
                found = true;
                break;
            }
            //Top Left
            if ( (TDM_LOC[i][0] == (int)x) && (TDM_LOC[i][1] == (int)y+1)) {
                y1 = y+1.0;
                found = true;
                break;
            }
            //Bottom Right
            if ( (TDM_LOC[i][0] == (int)x+1) && (TDM_LOC[i][1] == (int)y)) {
                x1 = x+1.0;
                found = true;
                break;
            }
            //Top Right
            if ( (TDM_LOC[i][0] == (int)x+1) && (TDM_LOC[i][1] == (int)y+1)) {
                x1 = x+1.0;
                y1 = y+1.0;
                found = true;
                break;
            }
        }
        if (!found) {
            printf("Error: can not find close TDM channel\n");
            return TCL_ERROR;    
        }


    } else if (type.compare("TDM") == 0) {

        if (XFREQ==0 || YFREQ==0 ||TDMDIST==0) {
            printf("Error: found TDM but the system is not initilized by ");
            return TCL_ERROR;    
        }

        x0 = (x+1)*XFREQ;
        x1 = x0;

        y0 = (y+1)*YFREQ;
        y1 = y0;

        z0 = z-TDMDIST;
        z1 = z+TDMDIST;

    } else
        return TCL_OK;


    // submit command to pipe
    memset(mycmd, 0, sizeof(mycmd));

    //sprintf(mycmd,"AddNetSeg(%s,%f,%f,%f,%f);",type.data(),x,y,z,track);
    if (argc>5)
        sprintf(mycmd,"AddLine(%f,%f,%f,%f,%f,%f,%f,%f,%f);",x0,y0,z0,x1,y1,z1,r,g,b);
    else
        sprintf(mycmd,"AddLine(%f,%f,%f,%f,%f,%f);",x0,y0,z0,x1,y1,z1);

    PIPE_FD = open(FIFO_FILE,O_WRONLY);
    write(PIPE_FD,mycmd,sizeof(mycmd));
    close(PIPE_FD);


    return TCL_OK;

}

// drawline x0 y0 z0 x1 y1 z1
int cg3d_drawLine(ClientData clientData,
                Tcl_Interp *interp,
                int argc, const char *argv[])
{

    printf("WARNING: this command is not used anymore\n");
    return TCL_OK;
    
    int x0,y0,z0;
    int x1,y1,z1;
    string drive;
    string load;
    bool isFound = false;
    int idx0,idx1;
    // get drive clb
    drive.assign(argv[1]);
    for (int i=0;i<v_clbs.size();i++) {
        if (drive.compare(v_clbs[i].getName()) == 0) {
            isFound = true;
            idx0 = i;
        }
    }

    if (!isFound) {
        printf("Error: Can not find driver clb %s\n",argv[1]);
        return TCL_ERROR;    
    }

    // get load clb
    load.assign(argv[2]);
    for (int i=0;i<v_clbs.size();i++) {
        if (load.compare(v_clbs[i].getName()) == 0) {
            isFound = true;
            idx1 = i;
        }
    }

    if (!isFound) {
        printf("Error: Can not find load clb %s\n",argv[2]);
        return TCL_ERROR;    
    }

    // prepare x,y,z
    v_clbs[idx0].getLoc(x0,y0,z0);
    v_clbs[idx1].getLoc(x1,y1,z1);

    //
    char mycmd[128];
    memset(mycmd, 0, sizeof(mycmd));
    
    sprintf(mycmd,"AddLine(%f,%f,%f,%f,%f,%f);",x0,y0,z0,x1,y1,z1);

    PIPE_FD = open(FIFO_FILE,O_WRONLY);
    printf("%s %d\n",mycmd,sizeof(mycmd));
    write(PIPE_FD,mycmd,sizeof(mycmd));
    close(PIPE_FD);
    
    return TCL_OK;
}

////////////////////////////////////////////////
// PHTREAD
////////////////////////////////////////////////
void *pipe_send_cmd_service(void* ptr) {
    
    char mycmd[128];
    //int idx = 0;
    while (1) {
        //printf("pipe_send_cmd_service %d\n",idx);
        //PIPE_FP = fopen(FIFO_FILE, "w");
        memset(mycmd, 0, sizeof(mycmd));

        pthread_mutex_lock( &mutex1 );
        
        if (SUB_CMDs.size() == 0) { // No operation
            //sprintf(mycmd,"NOP();");
            //printf("NOP\n");
            //fputs(mycmd, PIPE_FP);
        } else {
            sprintf(mycmd,"%s",SUB_CMDs.data());
            fputs(mycmd, PIPE_FP);
            printf("%s\n",SUB_CMDs.data());
            SUB_CMDs.clear();
        }
        pthread_mutex_unlock( &mutex1 );

        //fclose(PIPE_FP);
        //idx++;
    }
}

//////////////////////////////////////////////
//1.0 0.0 0.0 red, duh
//0.0 1.0 0.0 green, duh
//0.0 0.0 1.0 blue, duh
//1.0 1.0 0.0 yellow
//1.0 0.0 1.0 purple
//0.0 1.0 1.0 cyan
//1.0 1.0 1.0 white
// 
//1.0 0.5 0.0 orange
//0.5 1.0 0.0 greenish yellow
//...
//0.5 1.0 0.5 light green
//0.0 0.5 0.0 dark green
//...

bool _get_rgb_code(const char* ColorName,float& r,float& g,float& b) {
    string color(ColorName);

    ///{"BLACK","BLUE","RED","GREEN","YELLOW","GREY"};
    if (color.compare("BLACK") == 0) {
        r=0.0; g=0.0; b=0.0;
    } else if (color.compare("BLUE") == 0) {
        r=0.0; g=0.0; b=1.0;
    } else if (color.compare("RED") == 0) {
        r=1.0; g=0.0; b=0.0;
    } else if (color.compare("GREEN") == 0) {
        r=0.0; g=1.0; b=0.0;
    } else if (color.compare("YELLOW") == 0) {
        r=1.0; g=1.0; b=0.0;
    } else {
        return false;
    }
    return true;
    
}
